﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ASoft.Service
{
    public class MyTaskService
    {
        static MyTaskService()
        {
            Current = new MyTaskService();
        }

        public static MyTaskService Current { get; set; }

        CancellationTokenSource cts = new CancellationTokenSource();
        Action<Exception> exceptionAction;

        static object lockKey = new object();
        ConcurrentDictionary<Task, int> tasks = new ConcurrentDictionary<Task, int>();
        List<RealTimeItem> items = new List<RealTimeItem>();

        public void Start(Action<Exception> exceptionAction)
        {
            items.Clear();
            OnStarting();
            this.exceptionAction = exceptionAction;
            this.cts = new CancellationTokenSource();
            var t = Task.Factory.StartNew(() =>
            {
                while (!cts.IsCancellationRequested)
                {
                    DoTask();
                    Thread.Sleep(50);
                }
            }, cts.Token);
            tasks[t] = 0;
        }

        void DoTask()
        {
            var task = Task.Factory.StartNew(() =>
            {
                while (cts.IsCancellationRequested)
                {
                    return;
                }
                RealTimeItem doItem = null;
                lock (lockKey)
                {
                    doItem = items.Where(p => p.Time < DateTime.Now).OrderBy(p => p.Time).FirstOrDefault();
                    if (doItem != null)
                    {
                        items.Remove(doItem);
                    }
                }
                Thread t = Thread.CurrentThread;
                using (cts.Token.Register(t.Abort))
                {
                    if (doItem != null)
                        doItem.Action();
                }
            }, cts.Token);
            tasks[task] = 0;

            task.ContinueWith(x =>
            {
                int outValue = 0;
                tasks.TryRemove(task, out outValue);

                if (x.Exception == null)
                {
                    return;
                }

                var ex = x.Exception.InnerException;
                exceptionAction(ex);

            });


        }



        //public void Start(Action<Exception> exceptionAction)
        //{
        //    this.exceptionAction = exceptionAction;
        //    //
        //    Task.Factory.StartNew(() =>
        //    {
        //        var task = GetSubTask();
        //        while (!cts.IsCancellationRequested)
        //        {
        //            while (!task.IsCompleted)
        //            {
        //                Thread.Sleep(50);
        //            }
        //            //Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
        //            if (cts.IsCancellationRequested)
        //            {
        //                break;
        //            }
        //            task = GetSubTask();
        //        }
        //        this.exceptionAction = null;
        //        this.cts = new CancellationTokenSource();
        //    }, cts.Token);
        //}

        //Task GetSubTask()
        //{
        //    var task = Task.Factory.StartNew(() =>
        //    {
        //        var doItem = items.Where(p => p.Time < DateTime.Now).OrderBy(p => p.Time).FirstOrDefault();
        //        if (doItem != null)
        //        {
        //            doItem.Action();
        //            lock (lockKey)
        //            {
        //                items.Remove(doItem);
        //            }
        //        }
        //        else
        //        {
        //            Thread.Sleep(50);
        //        }
        //    })
        //    .ContinueWith(x =>
        //    {
        //        //x.Exception.Flatten();
        //        if (x.Exception == null)
        //            return;
        //        var ex = x.Exception.InnerException;
        //        exceptionAction(ex);
        //    });
        //    return task;
        //}

        public void Stop()
        {
            cts.Cancel(true);
            try
            {
                Task.WaitAll(tasks.Keys.ToArray());
            }
            catch (AggregateException e)
            {

                RC.Current.Loger.Trace("\nAggregateException thrown with the following inner exceptions:");
                // Display information about each exception.  
                foreach (var v in e.InnerExceptions)
                {
                    if (v is TaskCanceledException)
                        RC.Current.Loger.Trace(string.Format("   TaskCanceledException: Task {0}",
                                          ((TaskCanceledException)v).Task.Id));
                    else
                        RC.Current.Loger.Trace(string.Format("   Exception: {0}", v.GetType().Name));
                }
                Console.WriteLine();
            }

            // Display status of all tasks.  
            foreach (var task in tasks.Keys)
                RC.Current.Loger.Trace(string.Format("Task {0} status is now {1}", task.Id, task.Status));

            tasks = new ConcurrentDictionary<Task, int>();

            OnEnding();


        }
        public void AddItem(RealTimeItem item)
        {
            lock (lockKey)
            {
                items.Add(item);
            }
        }

        public void AddItem(DateTime time, Action action, string description)
        {
            AddItem(new RealTimeItem(time, action, description));
        }

        public event EventHandler<EventArgs> Starting;

        public event EventHandler<EventArgs> Ending;

        void OnStarting()
        {
            if (Starting != null)
            {
                Starting(this, EventArgs.Empty);
            }
        }

        void OnEnding()
        {
            if (Ending != null)
            {
                Ending(this, EventArgs.Empty);
            }
        }
    }
}
